React で Highcharts で作成したグラフを PDF のプレビュー上に表示させる
Highcharts というグラフ用のライブラリを使って、グラフを生成し PDF で 上に表示させてみます。
前提としては React を使用しており、ビルドツールには Vite を使用しています。
デモ
準備
上記のテンプレートを使用します。
Highcharts
Highcharts を追加します。今回は React を使っているので highcharts-react-official
も追加しています。
yarn add highcharts highcharts-react-official
Highcharts は 最終的なグラフを SVG として出力してくれます。綺麗でかつデモやドキュメントも豊富で使いやすいライブラリなのですが、商用の場合には有料なので採用を検討する場合には注意が必要です。
代替のライブラリとしては React Google Charts や Recharts などがあります。
react-pdf
react-pdf を追加します。今回は PDF を作成したいので使用するのは @react-pdf/renderer になります。
yarn add @react-pdf/renderer
ただし Vite でこれを import すると下記のようなエラーが出ます。
Uncaught ReferenceError: global is not defined
そのため vite-plugin-shim-react-pdf も使用します。
// vite.config.ts import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; import shimReactPdf from "vite-plugin-shim-react-pdf"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react(), shimReactPdf()], });
以上で準備は完了です。
Highcharts のグラフを PDF で表示させる
react-pdf には SVG をそのまま表示させることが可能です。
可能ではあるのですが、Highcharts の SVG を JSX に変換しても表示が崩れてしまいます。
そのため、Highcharts で生成される SVG を <canvas>
に表示させ toDataURL()
を使って PNG に変換したものを PDF 上で表示させることにします。
type Props = Readonly< { scale?: number; } & PropsWithoutRef<HighchartsReact.Props> >; export const Chart = (props: Props): JSX.Element => { const { scale = 3 } = props; const { width, height } = props.options.chart; const chartRef = useRef<HighchartsReact.Props["ref"]>(null); const canvasRef = useRef<HTMLCanvasElement>(null); return ( <> <HighchartsReact ref={chartRef} {...props} /> <canvas ref={canvasRef} width={width * scale} height={height * scale} ></canvas> </> ); };
canvas のサイズを大きくし出力する PNG のサイズを大きくすると、PDF 上で綺麗に表示されるため、ここではデフォルトを 3 倍として拡大したものを canvas 上に描画させます。
scale=1 | scale=3 |
---|---|
グラフはちょっとわかりにくいですが、文字だとscale=1のときにちょっとぼやけているのがわかるでしょうか。
あとはこのコンポーネントの useEffect
内で、SVG を PNG に変換する処理を記述するだけです。
useEffect(() => { const canvas = canvasRef.current; if (!canvas) { return; } const context = canvas.getContext("2d"); if (!context) { return; } const svg = chartRef.current.container.current.querySelector("svg"); const image = new Image(); const svgData = new XMLSerializer().serializeToString(svg); const base64 = btoa(unescape(encodeURIComponent(svgData))); image.src = `data:image/svg+xml;charset=utf-8;base64,${base64}`; const handleLoad = () => { context.drawImage(image, 0, 0, width * scale, height * scale); setImg(canvas.toDataURL()); }; image.addEventListener("load", handleLoad); return () => { image.removeEventListener("load", handleLoad); }; }, [width, height, setImg, scale]);
以上で Highcharts のグラフを PDF 上に表示できました。